//用于处理ectd相关任务的CRUD
package master

import (
	"context"
	"encoding/json"
	"fmt"
	"gitee.com/Cookie_XiaoD/crontab/common"
	"github.com/coreos/etcd/clientv3"
	"time"
)

type JobMgr struct {
	client *clientv3.Client
	lease  clientv3.Lease
	kv     clientv3.KV
}

var (
	G_JobMgr *JobMgr
)

//
func InitJobMgr() (err error) {
	//初始化任务管理器，配置etcd，获取连接以及租期操作
	var (
		cfg    clientv3.Config
		client *clientv3.Client
		lease  clientv3.Lease
		kv     clientv3.KV
	)

	cfg = clientv3.Config{
		DialTimeout: time.Duration(G_config.EtcdDialTimeout) * time.Millisecond,
		Endpoints:   G_config.EtcdEndpoints,
	}

	if client, err = clientv3.New(cfg); err != nil {
		return
	}

	fmt.Println("Etcd连接成功")

	kv = clientv3.NewKV(client)

	lease = clientv3.NewLease(client)

	G_JobMgr = &JobMgr{
		kv:     kv,
		client: client,
		lease:  lease,
	}

	return
}

func (jobMgr *JobMgr) SaveJob(job *common.Job) (oldJob *common.Job, err error) {
	var (
		jobVal  []byte
		putResp *clientv3.PutResponse
	)

	//序列化job对象

	if jobVal, err = json.Marshal(job); err != nil {
		return
	}

	//保存到etcd中，同时获取旧值
	jobKey := common.JOB_SAVE_DIR+ job.Name
	if putResp, err = jobMgr.kv.Put(context.TODO(), jobKey, string(jobVal), clientv3.WithPrevKV()); err != nil {
		return
	}

	//保存成功，序列化旧值
	if putResp.PrevKv == nil {
		return
	}

	//如果旧值序列化失败，没有影响，重置err为nil
	var tmpOld common.Job
	if err = json.Unmarshal(putResp.PrevKv.Value, &tmpOld); err != nil {
		err = nil
		return
	}

	oldJob=&tmpOld

	return
}




func (jobMgr *JobMgr) DeleteJob(jobName string) (oldJob *common.Job, err error) {
	var (
		delResp *clientv3.DeleteResponse
	)


	//删除etcd中指定key的内容，同时获取旧值
	jobKey := common.JOB_SAVE_DIR + jobName
	if delResp, err = jobMgr.kv.Delete(context.TODO(),jobKey,clientv3.WithPrevKV()); err != nil {
		return
	}

	//删除成功，序列化旧值
	if delResp.PrevKvs == nil {
		return
	}

	//如果旧值序列化失败，没有影响，重置err为nil
	var tmpOld common.Job
	if err = json.Unmarshal(delResp.PrevKvs[0].Value, &tmpOld); err != nil {
		err = nil
		return
	}

	oldJob=&tmpOld

	return
}


func (jobMgr *JobMgr) ListJob() (jobs []*common.Job, err error) {

	getResp,err:= jobMgr.kv.Get(context.TODO(),common.JOB_SAVE_DIR,clientv3.WithPrefix())
	if err != nil {
		return
	}

	jobs=make([]*common.Job,0)
	for _,v:=range getResp.Kvs{
		tmpJob:=&common.Job{}
		if err = json.Unmarshal(v.Value, tmpJob); err != nil {
			err=nil
			continue
		}

		jobs=append(jobs,tmpJob)

	}


	return
}


func (jobMgr *JobMgr) KillJob(jobName string)(err error){

	//所以杀死任务，就是put一个数据到指定目录下，worker监听这个目录，一旦有相关任务的put信息，就结束任务
	//但是put的杀死任务的信息其实不需要一直留着，仅仅是一次性的，所以设置一个租约，1秒就过期
	//对于worker监听的话，会先收到put，再收到delete

	var (
		grantResp *clientv3.LeaseGrantResponse
		jobKey string
	)

	jobKey=common.JOB_KILL_DIR+jobName


	//创建1秒的租约
	if grantResp,err=jobMgr.lease.Grant(context.TODO(),1);err!=nil{
		return
	}

	leaseID:=grantResp.ID


	_,err=jobMgr.kv.Put(context.TODO(),jobKey,"",clientv3.WithLease(leaseID))

	return
}